Imports System.Math
Imports cmplib01
Imports System.Drawing.Drawing2D


Module modCampaignRunnerCommon

    Public Enum cmXorY
        cmX = 0
        cmY = 1
    End Enum

    ' button colors
    Public c_DISABLEDBUTTONBACKCOLOR As Color = Color.DarkGray
    Public c_DISABLEDBUTTONFONTCOLOR As Color = Color.DimGray
    Public c_ENABLEDBUTTONBACKCOLOR As Color = Color.LightSkyBlue
    Public c_ENABLEDBUTTONFONTCOLOR As Color = Color.Black
    Public c_TOGGLEBUTTONINACTIVECOLOR As Color = Color.DarkSalmon
    Public c_TOGGLEBUTTONACTIVECOLOR As Color = Color.DarkSeaGreen

    Public Const c_Demo As Boolean = True
    Public Const c_GameSystemVersion As String = "0.2.3"
    Public Const c_IconHalfWidth As Integer = 16
    Public Const c_IconHalfHeight As Integer = 16
    Public Const c_Module As String = "modCampaignerCommon"



    Public Function CalculateAverageMaxPV(ByVal GCClass As GroundChitClass_cgd, ByVal game As cmplib01.CampaignerGameData) As Int32
        'Dim gcKey As String
        'Dim gc As GroundChitType
        Dim gcCount As Int16
        Dim totalPV As Int32
        Dim calculatedAverage As Int32

        Try
            For Each mem As ChitMember In game.Chits
                If mem.UnitType = GroundChitClass_cgd.Battlegroup Then
                    gcCount += 1
                    totalPV += DirectCast(mem, BattlegroupChit).deployedpointvalue
                End If

            Next

            If gcCount <> 0 Then
                If totalPV <> 0 Then
                    calculatedAverage = totalPV / gcCount
                End If
            End If
        Catch ex As Exception
            HandleException(c_Module, ex)
        End Try

        Return calculatedAverage
    End Function

    Public Function CalculateMaxDeployedPV(ByVal GCClass As GroundChitClass_cgd, ByRef game As CampaignerGameData) As Int32
        Dim maxPV As Int32

        Try
            For Each mem As ChitMember In game.Chits
                If mem.UnitType = GroundChitClass_cgd.Battlegroup Then
                    If maxPV < DirectCast(mem, BattlegroupChit).deployedpointvalue Then maxPV = DirectCast(mem, BattlegroupChit).deployedpointvalue
                End If
            Next
        Catch ex As Exception
            maxPV = 0
        End Try

        Return maxPV
    End Function

    Private Sub CalculateCurrentDeployedPV(ByRef game As CampaignerGameData)
        Dim fact As Faction
        Dim factKey As String
        Dim dep As Long

        Try
            For Each factKey In game.Faction_g.HT.Keys
                fact = game.Faction_g.Item(factKey)

                For Each mem As ChitMember In game.Chits

                    If mem.OwningFaction.Equals(fact) Then
                        If mem.UnitType = GroundChitClass_cgd.Battlegroup Then
                            If Not mem.Status = GroundUnitStatus_cgd.Dead Then dep += DirectCast(mem, BattlegroupChit).CurrentPV
                        End If
                    End If
                Next

                fact.FactionDeployedPoints = dep
                dep = 0
            Next

        Catch ex As Exception
            HandleException(c_Module, ex)
        End Try

    End Sub


    Public Function XYDistance(ByVal X1 As Integer, ByVal X2 As Integer, ByVal Y1 As Integer, ByVal Y2 As Integer) As Integer
        'returns the distance between two 2d coordinates
        XYDistance = Int(Abs(Math.Sqrt(((X1 - X2) * (X1 - X2)) + ((Y1 - Y2) * (Y1 - Y2)))))

    End Function

    Public Function XYDistance(ByVal pnt1 As Point, ByVal pnt2 As Point) As Integer
        Return XYDistance(pnt1.X, pnt2.X, pnt1.Y, pnt2.Y)
    End Function

    Public Function XYDistanceSingle(ByVal pnt1 As Point, ByVal pnt2 As Point) As Single
        Return Abs(Math.Sqrt(((pnt1.X - pnt2.X) * (pnt1.X - pnt2.X)) + ((pnt1.Y - pnt2.Y) * (pnt1.Y - pnt2.Y))))
    End Function

    Public Sub DisableButton(ByRef btn As Button)
        btn.BackColor = c_DISABLEDBUTTONBACKCOLOR
        btn.ForeColor = c_DISABLEDBUTTONFONTCOLOR
        btn.Enabled = False
    End Sub

    Public Sub EnableButton(ByRef btn As Button)
        btn.BackColor = c_ENABLEDBUTTONBACKCOLOR
        btn.ForeColor = c_ENABLEDBUTTONFONTCOLOR
        btn.Enabled = True
    End Sub

    Public Sub DrawZOC(ByVal gr As Graphics, ByVal pnt As Point, ByVal radius As Int16, Optional ByVal enemy As Boolean = False, Optional ByVal status As GroundUnitStatus_cgd = GroundUnitStatus_cgd.OK)

        Try
            If radius = 0 Then Exit Sub
            Dim c1(2) As Byte

            '// if the status is "funny" we will use a reduced radius as well.
            Select Case status
                Case GroundUnitStatus_cgd.Dead, GroundUnitStatus_cgd.Disarrayed, GroundUnitStatus_cgd.Assembling, GroundUnitStatus_cgd.Not_Deployed, GroundUnitStatus_cgd.Scattered, GroundUnitStatus_cgd.Regrouping
                    '// no ZOC
                    Exit Sub
                Case GroundUnitStatus_cgd.OK, GroundUnitStatus_cgd.Regrouped
                    '// normal radius and green
                    c1(0) = 75
                    c1(1) = 255
                    c1(2) = 75
                Case Else
                    '// 1/2 radius and yellow
                    radius /= 2
                    c1(0) = 255
                    c1(1) = 255
                    c1(2) = 0
            End Select

            pnt.X -= radius / 2
            pnt.Y -= radius / 2

            If enemy Then
                gr.DrawEllipse(New Pen(Color.FromArgb(255, 255, 75, 75)), pnt.X, pnt.Y, radius, radius)
                gr.FillEllipse(New SolidBrush(Color.FromArgb(100, 255, 75, 75)), pnt.X, pnt.Y, radius, radius)

            Else
                gr.DrawEllipse(New Pen(Color.FromArgb(255, c1(0), c1(1), c1(2))), pnt.X, pnt.Y, radius, radius)
                gr.FillEllipse(New SolidBrush(Color.FromArgb(100, c1(0), c1(1), c1(2))), pnt.X, pnt.Y, radius, radius)
            End If
        Catch ex As Exception
            HandleException(c_Module, ex)
        End Try

    End Sub

    Public Sub DrawBorderedCircle(ByVal gr As Graphics, ByVal pnt As Point, ByVal radius As Int16, ByVal outterColor As Color, ByVal innerColor As Color)
        Dim outter As New Pen(outterColor)
        Dim inner As New Pen(innerColor)

        pnt.X -= radius / 2
        pnt.Y -= radius / 2

        gr.DrawEllipse(outter, pnt.X, pnt.Y, radius, radius)
        gr.DrawEllipse(inner, pnt.X + 1, pnt.Y + 1, radius - 2, radius - 2)
        gr.DrawEllipse(outter, pnt.X + 2, pnt.Y + 2, radius - 4, radius - 4)


    End Sub

    Public Sub DrawFilledRectangle(ByVal gr As Graphics, ByVal x As Single, ByVal y As Single, ByVal width As Single, ByVal height As Single)
        gr.FillRectangle(Brushes.Blue, x, y, width, height)

    End Sub

    Const pi As Double = 3.14159265358979

    Public Function GetXorYStrength(ByRef XorY As cmXorY, ByVal X1 As Single, ByVal X2 As Single, ByVal Y1 As Single, ByVal Y2 As Single) As Single
        'returns the x strength from X1 to X2
        'Determine Bearing and assign X and Y Strength
        Dim iQuadrant As Integer
        Dim XStrength As Single
        Dim YStrength As Single
        Dim X As Single
        Dim Y As Single
        Dim k As Single
        Dim a As Single
        Dim Degrees As Single

        iQuadrant = FindTargetQuad(X1, X2, Y1, Y2)

        Y = Y1 - Y2
        X = X1 - X2

        'Determine Degrees and XY Strength
        Select Case iQuadrant
            Case Is = 1
                Degrees = 360
                XStrength = 0
                YStrength = -1
            Case Is = 3
                Degrees = 90
                XStrength = 1
                YStrength = 0
            Case Is = 5
                Degrees = 180
                XStrength = 0
                YStrength = 1
            Case Is = 7
                Degrees = 270
                XStrength = -1
                YStrength = 0
            Case Is = 2
                k = X / Y
                a = Math.Atan(k)  'yields radians
                Degrees = (Abs(a * (180 / pi)))  'Converts Radians to Degrees
                XStrength = Degrees / 90
                YStrength = (1 - XStrength) * -1
            Case Is = 4
                k = Y / X 'X / Y
                a = Math.Atan(k)   'yields radians
                Degrees = 180 - (Abs(a * (180 / pi))) 'Converts Radians to Degrees
                XStrength = (Degrees - 90) / 90
                YStrength = 1 - XStrength
            Case Is = 6
                k = X / Y
                a = Math.Atan(k)   'yields radians
                Degrees = Abs(a * (180 / pi)) + 180 'Converts Radians to Degrees
                XStrength = -1 * (Degrees - 180) / 90
                YStrength = 1 + XStrength
            Case Is = 8
                k = X / Y
                a = Math.Atan(k)   'yields radians
                Degrees = 360 - (Abs(a * (180 / pi))) 'Converts Radians to Degrees
                YStrength = -1 * ((Degrees - 270) / 90)
                XStrength = -1 * (1 + YStrength)
            Case Is = 0
                Degrees = 0
        End Select

        If XorY = 0 Then
            GetXorYStrength = XStrength
        Else
            GetXorYStrength = YStrength
        End If

    End Function

    Public Sub GetXYStrength(ByVal srcX As Single, ByVal srcY As Single, ByVal destX As Single, ByVal destY As Single, ByRef xStr As Single, ByRef yStr As Single)
        xStr = GetXorYStrength(cmXorY.cmX, srcX, destX, srcY, destY)
        yStr = GetXorYStrength(cmXorY.cmY, srcX, destX, srcY, destY)
    End Sub

    Public Sub GetXYStrength(ByVal srcPoint As Point, ByVal destPoint As Point, ByRef xStr As Single, ByRef yStr As Single)
        xStr = GetXorYStrength(cmXorY.cmX, srcPoint.X, destPoint.X, srcPoint.Y, destPoint.Y)
        yStr = GetXorYStrength(cmXorY.cmY, srcPoint.X, destPoint.X, srcPoint.Y, destPoint.Y)
    End Sub

    Private Function FindTargetQuad(ByVal X1 As Integer, ByVal X2 As Integer, ByVal Y1 As Integer, ByVal Y2 As Integer) As Integer
        'Returns the 'quadrant' of the target object (x2,y2) in respect to the source (x1,y1)
        'This function assists GetXorYStrength to return the x or y strength
        Dim XResult As Single
        Dim YResult As Single

        XResult = X1 - X2
        YResult = Y1 - Y2
        Select Case XResult
            Case Is > 0         'Xresult positive
                Select Case YResult
                    Case Is > 0 'And Yresult positive
                        FindTargetQuad = 8
                    Case Is < 0 'And Yresult negative
                        FindTargetQuad = 6
                    Case Is = 0 'And Yresult Zero
                        FindTargetQuad = 7
                End Select
            Case Is < 0         'Xresult negative
                Select Case YResult
                    Case Is > 0 'And Yresult Positive
                        FindTargetQuad = 2
                    Case Is < 0 'And Yresult Negative
                        FindTargetQuad = 4
                    Case Is = 0 'And Yresult Zero
                        FindTargetQuad = 3
                End Select
            Case Is = 0         'Xresult Zero
                Select Case YResult
                    Case Is > 0 'And Yres positive
                        FindTargetQuad = 1
                    Case Is < 0 'And Yres Negative
                        FindTargetQuad = 5
                    Case Is = 0 'And Yres Zero
                        FindTargetQuad = 9
                End Select
        End Select

    End Function

    Public Sub ReassignChitToPlayerID(ByRef chit As ChitMember, ByVal newPlayerID As Int64)

    End Sub

    Public Sub DeleteChit(ByRef Game As CampaignerGameData, ByRef chit As ChitMember)

    End Sub

    Public Sub SetNewPointValue(ByRef chit As ChitMember, ByVal newPointValue As Int32)

    End Sub

    Public Sub PaintDanger(ByRef ctl As Control)
        Dim g As Graphics = ctl.CreateGraphics
        Dim hbrush As New HatchBrush(HatchStyle.WideUpwardDiagonal, Color.Black, Color.Yellow)
        g.FillRectangle(hbrush, New RectangleF(0, 0, ctl.Width, ctl.Height))

        hbrush.Dispose()
        g.Dispose()
    End Sub

#Region "Management Subs"

    Public Sub ManageElements(ByRef SelectedChit As ChitMember, ByRef ptrGameData As CampaignerGameData)
        Dim fManage As New frmElementManager(ptrGameData, SelectedChit)
        fManage.ShowDialog()
    End Sub

    Public Sub RenameChit(ByRef SelectedChit As ChitMember)
        'Dim key As String

        ''If SelectedChit.GroundChitClass = GroundChitClass_cgd.Objective Then
        ''    key = SelectedChit.Key & ":" & SelectedChit.Location.X & "-" & SelectedChit.Location.Y
        ''Else
        ''    key = SelectedChit.Key
        ''End If

        '' give the good ole basic entry.
        'Dim fEntry As New frmBasicEntry("Enter a new name.", "[a-z]")
        'fEntry.ShowDialog()

        'If fEntry.Cancelled = False Then
        '    Dim newName As String = fEntry.Value
        '    SelectedChit.Name = newName
        'End If

    End Sub

    Public Sub DeleteChit(ByRef SelectedChit As ChitMember, ByRef ptrGameData As CampaignerGameData)
        ' cleanly deletes the chit. Removes it from the universe. See ya!

        If MessageBox.Show("Delete Chit?", "Are you sure you wish to completely remove this chit?", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = DialogResult.Yes Then
            ptrGameData.Chits.remove(SelectedChit)
        End If

    End Sub

    Public Sub KillChit(ByRef SelectedChit As ChitMember)
        ' converts the chit to dead status. lol. DEAD!!!
        SelectedChit.Status = GroundUnitStatus_cgd.Dead

        If SelectedChit.UnitType = GroundChitClass_cgd.Battlegroup Then
            DirectCast(SelectedChit, BattlegroupChit).waypoints.Clear()
        End If

        SelectedChit.Status = GroundUnitStatus_cgd.Dead

    End Sub

    Public Sub ReassignOwner(ByRef SelectedChit As ChitMember, ByRef ptrGameData As CampaignerGameData)
        Dim fP As New frmPlayerSelect(ptrGameData)
        fP.ShowDialog()

        Try
            If fP.Cancelled = False Then
                ' reassign the chit
                SelectedChit.OwningPlayer = fP.SelectedPlayer
                ' oooo.... what about the faction id eh?
                Dim fact As Faction
                fact = ptrGameData.Faction_g.Item(fP.SelectedPlayer.FactionKey)
                SelectedChit.OwningFaction = fact
            End If
        Catch ex As Exception
            HandleException(c_Module, ex)
        End Try
    End Sub

    Public Sub ChangeMovementValue(ByRef SelectedChit As ChitMember)
        'Dim fEditMove As New frmEditMovementValues(SelectedChit)
        'fEditMove.ShowDialog()

    End Sub

    Public Sub ChangePointValue(ByRef SelectedChit As BattlegroupChit)
        Dim sPat As String = "\b[0-9]" ' only numbers
        Dim fNewVal As New frmBasicEntry("Enter Numeric Value [" & SelectedChit.CurrentPV & "/" & SelectedChit.deployedpointvalue & "]", sPat)

        fNewVal.ShowDialog()

        If Not fNewVal.Cancelled Then
            ' set the point value
            SelectedChit.CurrentPV = fNewVal.Value
        End If

    End Sub
#End Region


End Module

Public Class ChitTreeNode
    Inherits TreeNode

    Private m_GroundChit As ChitMember

    Public Sub New(ByRef GC As ChitMember)
        MyBase.New()
        Me.Text = GC.Name
        Me.Tag = GC.Name
        m_GroundChit = GC
    End Sub

    Public ReadOnly Property Member() As ChitMember
        Get
            Return m_GroundChit
        End Get
    End Property
End Class

Public Class ChitRuleTreeNode
    ' used to add a little more functionality to the treenode
    Inherits TreeNode

    Private m_Chit As ChitMember
    Private m_rtype As RuleType
    Private m_rule As Rule '// the instance of the rule


    Public Sub New()
        MyBase.New()
    End Sub

    Public Sub New(ByVal Chit As ChitMember)
        MyBase.New()
        m_Chit = Chit
    End Sub

    Public Sub New(ByVal Rtype As RuleType, ByVal ru As Rule)
        MyBase.New()
        m_rtype = Rtype
        m_rule = ru
    End Sub

    Public Sub New(ByVal NodeText As String, ByVal Chit As ChitMember, ByVal RType As RuleType, ByVal ru As Rule)
        MyBase.New()
        Me.Text = NodeText
        m_Chit = Chit
        m_rtype = RType
        m_rule = ru
    End Sub

    Public Property Chit() As ChitMember
        Get
            Return m_Chit
        End Get
        Set(ByVal Value As ChitMember)
            m_Chit = Value
        End Set
    End Property

    Public Property RType() As RuleType
        Get
            Return m_rtype
        End Get
        Set(ByVal value As RuleType)
            m_rtype = value
        End Set
    End Property

    Public Property Ru() As Rule
        Get
            Return m_rule
        End Get
        Set(ByVal value As Rule)
            m_rule = value
        End Set
    End Property

End Class




'// mikes vector stuff
Public Class SMVector
    Private m_dx As Single
    Private m_dy As Single
    Private m_StPt As PointF

    Public Property StartPoint() As PointF
        Get
            Return m_StPt
        End Get
        Set(ByVal Value As PointF)
            m_StPt = Value
        End Set
    End Property

    Public Function Distance(ByVal dx As Double, ByVal dy As Double) As Double

        Distance = (dx ^ 2 + dy ^ 2) ^ 0.5

    End Function

    Public Function VectorAngle(ByVal dx As Double, ByVal dy As Double) As Double
        Dim dblVecDist As Double

        dblVecDist = Me.Distance(dx, dy)

        If dx > 0 And dy > 0 Then 'q1
            Return Math.Atan2(dy, dx)
            'Return Math.Cos(dx / dblVecDist)
        ElseIf dx < 0 And dy > 0 Then 'q2
            Return Math.Atan2(dy, dx)
        ElseIf dx < 0 And dy < 0 Then 'q3
            Return Math.Atan2(dy, dx)
        ElseIf dx > 0 And dy < 0 Then 'q4
            Return Math.Atan2(dy, dx)
        ElseIf dx = 0 And dy < 0 Then
            Return 3 * PI / 2
        ElseIf dx = 0 And dy > 0 Then
            Return PI / 2
        ElseIf dy = 0 And dx > 0 Then
            Return 0.0
        ElseIf dy = 0 And dx < 0 Then
            Return PI
        End If

    End Function

    Public Function GeneratePtOnVector(ByVal StPt As PointF, ByVal EndPt As PointF, ByVal distFromStPt As Single) As PointF

        'Given D = distance
        'ax + by + D = 0
        'cos (theta) = x/D
        'X = D * cos(theta)
        'Y = D * sin (theta)
        'sin (theta) = y/D
        'pythagorean 
        '(x)^2 + (y) ^2 = (D) ^2
        'x = (D^2 - y^2)^.5
        'y = (D^2 - x^2)^.5
        Dim dx As Double = EndPt.X - StPt.X
        Dim dy As Double = EndPt.Y - StPt.Y

        Dim theta As Double = VectorAngle(dx, dy)

        Dim x As Double = distFromStPt * Math.Cos(theta)
        Dim y As Double = distFromStPt * Math.Sin(theta)

        GeneratePtOnVector = New PointF(CType(StPt.X + x, Single), CType(StPt.Y + y, Single))

    End Function
    Public Property DX() As Single
        Get
            Return m_dx
        End Get
        Set(ByVal Value As Single)
            m_dx = Value
        End Set
    End Property
    Public Property DY() As Single
        Get
            Return m_dy
        End Get
        Set(ByVal Value As Single)
            m_dy = Value
        End Set
    End Property
    Public Sub New(ByVal dx As Single, ByVal dy As Single)
        m_dx = dx
        m_dy = dy
    End Sub
    Public Sub New(ByVal dx As Single, ByVal dy As Single, ByVal stPt As PointF)
        m_dx = dx
        m_dy = dy
        Me.StartPoint = stPt
    End Sub
    Public Sub New()

    End Sub
    Public Function Subtract(ByVal vec1 As SMVector, ByVal vec2 As SMVector) As SMVector
        Dim vecOut As SMVector
        vecOut = New SMVector
        vecOut.DX = vec2.DX - vec1.DX
        vecOut.DY = vec2.DY - vec1.DY
        Return vecOut
    End Function

    Public Function Add(ByVal vec1 As SMVector, ByVal vec2 As SMVector) As SMVector

        Dim vecOut As SMVector
        vecOut = New SMVector
        vecOut.DX = vec1.DX + vec2.DX
        vecOut.DY = vec1.DY + vec2.DY
        Return vecOut

    End Function
End Class

